﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace FolderSynchronizer
{
	/// <summary>
	/// Класс, в котором реализуются методы обновления папок.
	/// </summary>
	public class Synchronizer : ISynchronizer
	{
		/// <summary>
		/// Путь к исходному каталогу
		/// </summary>
		public string SourceFolder { get; private set; }

		/// <summary>
		/// Путь к конечному каталогу.
		/// </summary>
		public string TargetFolder { get; private set; }

		/// <summary>
		/// Пути к каталогам, которые должны быть очищены перед копированием в них.
		/// </summary>
		public string[] PureFolders { get; private set; }

		/// <summary>
		/// Пути к каталогам, которые нальзя удалять.
		/// </summary>
		public string[] ExcludeFolders { get; private set; }

		/// <summary>
		/// Относительные пути к файлам в конечном каталоге, которые не нужно обновлять.
		/// </summary>
		public string[] ExcludeFiles { get; private set; }

		/// <summary>
		/// Список файлов для копирования.
		/// </summary>
        public List<FileInformation> FilesForCopy { get; private set; }

		/// <summary>
		/// Полность ли совпадают два каталога после копирования
		/// </summary>
		public bool FullMatchDirectories { get; private set; }

        /// <summary>
        /// Событие : Файлы в промежуточном каталоге.
        /// </summary>
        public Action InTempFolder { get; private set; }

		/// <summary>
		/// Путь к файлу с информацией о файлах в релизе.
		/// </summary>
		public string CheckSumFile { get; private set; }

		/// <summary>
		/// Количество скопированных файлов.
		/// </summary>
		public int CountOfCopedFiles { get; set; }

		/// <summary>
		/// Путь к промежуточному каталогу.
		/// </summary>
		public string PathToTempFolder { get; private set; }

		/// <summary>
		/// Массив с информацие о файлах в исходном каталоге.
		/// </summary>
		public FileInfo[] SourcePathFiles { get; private set; }

		/// <summary>
		/// Массив с информацией  о файлах  в конечном каталоге.
		/// </summary>
		public List<FileInfo> TargetPathFiles { get; private set; }

		/// <summary>
		/// Список директорий исходной папки.
		/// </summary>
		public List<DirectoryInfo> TargetDirInfo { get; private set; }

        /// <summary>
        /// Конфигурация синхронизации.
        /// </summary>
        public SynchronizerSettings Settings { get; private set; }

		/// <summary>
		/// Конструктор.
		/// </summary>
		/// <param name="settings">Конфигурация синхронизации.</param>
		public Synchronizer(SynchronizerSettings settings)
		{
		    Settings = settings;
			SourceFolder = settings.SourceFolder;
			TargetFolder = settings.TargetFolder;
            PathToTempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
			SourcePathFiles = new DirectoryInfo(SourceFolder).GetFiles(".", SearchOption.AllDirectories);
			ExcludeFiles = settings.ExcludeFiles;
			ExcludeFolders = settings.ExcludeFolders;
			PureFolders = settings.PureFolders;
			FilesForCopy = settings.FilesForCopy;
			FullMatchDirectories = settings.FullMatchDirectories;
			CheckSumFile = settings.CheckSumFile;
			TargetPathFiles = new List<FileInfo>();
            InTempFolder = settings.InTempFolder;
		}

		/// <summary>
		/// Копирование файлов с использованием вспомогательной папки.
		/// </summary>
		public void Synchronize()
		{
			ChackExistenceBar();
			Ignoring();
			ChangeAttributes();
			ChackOnAcces();

            try
            {
                CopyToTempFolder();
                CopyToTargetFolder();
            }
            finally
            {
                DeleteFolder(PathToTempFolder);
            }
		}

		/// <summary>
		/// Удаление папки.
		/// </summary>
		/// <param name="tmp">Путь к удаляемой папке.</param>
		public void DeleteFolder(string tmp)
		{
			if (Directory.Exists(tmp))
			{
				Utils.SetAttribute(tmp, FileAttributes.Normal);
				Directory.Delete(tmp, true);
			}
		}

		private void ChackExistenceBar()
		{
			if (Settings.Bar == null)
			{
				throw new SynchronizerException(SynchronizerException.ErrorType.DelegateNotFounded);
			}
		}

		private void CopyToTempFolder()
		{
			var copyToTemp = new TempStager(this);
			copyToTemp.Start();
		}

		private void CopyToTargetFolder()
		{
			var copyToTarget = new TargetStager(this);
			copyToTarget.Start();
		}

		private void Ignoring()
		{
			if (Directory.Exists(TargetFolder))
			{
			    var ignorer = new Ignorer(TargetFolder, ExcludeFolders);
			    TargetDirInfo = ignorer.GetDirectories();
			    TargetPathFiles = ignorer.GetFiles();
			}
		}

		private void ChackOnAcces()
		{
			foreach (FileInfo file in TargetPathFiles)
			{
				try
				{
					using (FileStream stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None))
					{
						stream.Close();
					}
				}
				catch (IOException)
				{
					throw new SynchronizerException(file, SynchronizerException.ErrorType.CantGetWriteAccess);
				}
				catch (UnauthorizedAccessException)
				{
					throw new SynchronizerException(file, SynchronizerException.ErrorType.CantGetWriteAccess);
				}
			}
		}

		private void ChangeAttributes()
		{
			foreach (var file in TargetPathFiles)
			{
				FileAttributes attributes = File.GetAttributes(file.FullName);

				if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
				{
					file.Attributes = FileAttributes.Normal;
					File.SetAttributes(file.FullName, attributes & ~FileAttributes.ReadOnly);
				}
			}
		}
	}
}
